package org.orm.hsc.session;

import com.domain.Car;
import org.orm.hsc.Configuration;
import org.orm.hsc.JdbcExecutor;
import org.orm.hsc.SqlHandler;
import org.orm.hsc.exceptions.ResultCountException;
import org.orm.hsc.exceptions.SQLFormatException;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * @author
 * @date 2025-04-16
 * @Description
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    private JdbcExecutor jdbcExecutor;

    //---------------------未加入configuration-------------------------------
    public DefaultSqlSession() {
        this(false);
    }

    public DefaultSqlSession(boolean isAutoCommit) {
        // 创建JdbcExecutor
        jdbcExecutor = new JdbcExecutor(isAutoCommit);
    }


    //---------------------加入configuration-------------------------------

    public DefaultSqlSession(Configuration configuration) {
        this(configuration, false);
    }

    public DefaultSqlSession(Configuration configuration, boolean isAutoCommit) {
        this.configuration = configuration;
        // 创建JdbcExecutor
        jdbcExecutor = new JdbcExecutor(configuration, isAutoCommit);
    }

    /**
     * 实现数据保存后，需要获得自增主键值，并将其存储在paramObj对象的指定属性中
     *
     * @param sql
     * @param paramObj
     * @param propertyName 需要设置主键值的字段
     * @return
     */
    @Override
    public int insert(String sql, Object paramObj, String propertyName) {
        // 直接调用主键值获取方法
        int count = insert(sql, paramObj, true);
        // 处理关于自增主键值问题
        Long generatedKey = jdbcExecutor.getGeneratedKey();
        // 获取set方法名 下面反射需要
        String methodName;
        if (propertyName.length() == 1) {
            methodName = "set" + propertyName.toUpperCase();
        } else {
            methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
        }
        // 将返回的主键值 设置到提供的字段中
        // 通过反射获取到对象的所有的方法
        Method[] methods = paramObj.getClass().getMethods();
        for (Method method : methods) {
            // 获取当前方法的方法名换
            String mnname = method.getName();
            // 判断当前方法是否是提供过来保存主键值的set方法
            if (mnname.equalsIgnoreCase(methodName)) {
                try {
                    // 判断set方法中参数类型是什么样的 进行设置
                    Class<?> parameterType = method.getParameterTypes()[0];
                    if (parameterType == short.class || parameterType == Short.class) {
                        method.invoke(paramObj, generatedKey.shortValue());
                    }
                    if (parameterType == int.class || parameterType == Integer.class) {
                        method.invoke(paramObj, generatedKey.intValue());
                    }
                    if (parameterType == long.class || parameterType == Long.class) {
                        method.invoke(paramObj, generatedKey);
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
                break;
            }
        }
        return count;

    }

    @Override
    public int insert(String sql, Object paramObj, boolean isGeneratedKey) {
        // 判断是增删改方法还是查询方法
        if ("insert".equalsIgnoreCase(sql.trim().substring(0, 6))) {
            // 调用JdbcExecutor的doUpdate方法
            // 将主键值 放在 JdbcExecutor的generatedKeys中
            return jdbcExecutor.doUpdate(sql, isGeneratedKey, paramObj);
        }
        //代码至此，说明不是一个insert语句，不执行。 需要给使用者反馈（异常）
        throw new SQLFormatException("not a insert statement : " + sql);
    }

    @Override
    public int insert(String sql, Object paramObj) {
        // 默认是不返回主键值
        return this.insert(sql, paramObj, false);
    }

    @Override
    public int insert(String sql) {
        // 直接调用方法
        return this.insert(sql, null);
    }

    @Override
    public void commit() {
        jdbcExecutor.commit();
    }

    @Override
    public void rollback() {
        jdbcExecutor.rollback();
    }

    @Override
    public void Close() {
        jdbcExecutor.close();
    }

    @Override
    public int update(String sql, Object paramObj) {
        if ("update".equalsIgnoreCase(sql.trim().substring(0, 6))) {
            return jdbcExecutor.doUpdate(sql, false, paramObj);
        }
        throw new SQLFormatException("not a update statement : " + sql);
    }

    @Override
    public int update(String sql) {
        return this.update(sql, null);
    }

    @Override
    public int delete(String sql, Object paramObj) {
        if ("delete".equalsIgnoreCase(sql.trim().substring(0, 6))) {
            return jdbcExecutor.doUpdate(sql, false, paramObj);
        }
        throw new SQLFormatException("not a delete statement : " + sql);
    }

    @Override
    public int delete(String sql) {
        return this.delete(sql, null);
    }

    @Override
    public <T> List<T> selectList(String sql, Object paramObj, Class<T> rowType) {
        if ("select".equalsIgnoreCase(sql.trim().substring(0, 6))) {
            return jdbcExecutor.doQuery(sql, rowType, paramObj);
        }
        throw new SQLFormatException("not a select statement : " + sql);
    }

    @Override
    public <T> List<T> selectList(String sql, Class<T> rowType) {
        return this.selectList(sql, null, rowType);
    }

    @Override
    public <T> T selectOne(String sql, Object paramObj, Class<T> rowType) {
        List<T> list = selectList(sql, paramObj, rowType);
        if (list == null || list.size() == 0) {
            return null;
        } else if (list.size() == 1) {
            return list.get(0);
        } else {
            //找得了多条记录，说明存在问题，需要给与使用者反馈（异常）
            throw new ResultCountException("it was expected to be null or one , but it was " + list.size());
        }
    }

    @Override
    public <T> T selectOne(String sql, Class<T> rowType) {
        return selectOne(sql, null, rowType);
    }

    //---------------动态sql新增API 实现方式不一样----------------
    @Override
    public int save(String sqlid, Object paramObj) {
        // 根据sqlid 获取对应的处理类 SqlHandler 在获取SqlSession 时候就会进行初始化
        SqlHandler sqlHandler = configuration.getHandler(sqlid);
        // 从获取propertyName 用于设置主键的
        String propertyName = sqlHandler.getPropertyName();
        // 判断是否需要获取主键值
        // 无须获取主键值
        if (propertyName == null || "".equalsIgnoreCase(propertyName)) {
            // 不需要设置主键
            this.save(sqlid, paramObj, false);

        }
        // 执行sql
        int count = this.save(sqlid, paramObj, true);
        // 获取主键值
        Long generatedKey = jdbcExecutor.getGeneratedKey();
        // 处理将主键值放在给定的字段中
        String methodName;
        // 获取主键设置的set的方法
        if (propertyName.length() == 1) {
            methodName = "set" + propertyName.toUpperCase();
        } else {
            methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
        }
        // 通过反射获取到对象的所有的方法
        Method[] methods = paramObj.getClass().getMethods();
        // 过滤set方法
        for (Method method : methods) {
            String mnname = method.getName();
            if (mnname.equalsIgnoreCase(methodName)) {
                try {
                    // 获取set 方法参数类型
                    Class<?> parameterType = method.getParameterTypes()[0];
                    if (parameterType == short.class || parameterType == Short.class) {
                        method.invoke(paramObj, generatedKey.shortValue());
                    }
                    if (parameterType == int.class || parameterType == Integer.class) {
                        method.invoke(paramObj, generatedKey.intValue());
                    }
                    if (parameterType == long.class || parameterType == Long.class) {
                        method.invoke(paramObj, generatedKey);
                    }

                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                break;
            }
        }
        // 获取主键值

        return count;
    }


    /**
     * 保存的方法 不需要设置主键值
     *
     * @param sqlid
     * @param paramObj
     * @param isGeneratedKey
     * @return
     */
    public int save(String sqlid, Object paramObj, boolean isGeneratedKey) {
        // 获取对应的handler
        SqlHandler sqlHandler = configuration.getHandler(sqlid);
        String oldSql = sqlHandler.getOldSql();
        if ("insert".equalsIgnoreCase(oldSql.trim().substring(0, 6))) {
            return jdbcExecutor.executeUpdate(sqlid, isGeneratedKey, paramObj);
        }
        //代码至此，说明不是一个insert语句，不执行。 需要给使用者反馈（异常）
        throw new SQLFormatException("not a insert statement : " + oldSql);
    }

    @Override
    public int save(String sqlid) {
        return save(sqlid, null);
    }

    /**
     * 无条件查询
     *
     * @param sqlid
     * @return
     */
    @Override
    public List<Car> searchList(String sqlid) {
        // 方法重载
        return searchList(sqlid, null);
    }

    /**
     * 有条件查询
     *
     * @param sqlid
     * @param paramObj
     * @return
     */
    public <T> List<T> searchList(String sqlid, Object paramObj) {
        SqlHandler handler = configuration.getHandler(sqlid);
        String oldSql = handler.getOldSql();
        // SqlHandler 中已经设置了rowType
        Class rowType = handler.getRowType();
        if ("select".equalsIgnoreCase(oldSql.trim().substring(0, 6))) {
            return jdbcExecutor.doQuery(oldSql, rowType, paramObj);
        }
        throw new SQLFormatException("not a select statement : " + oldSql);
    }

    @Override
    public <T> T searchOne(String sqlid) {
        // 方法重载
        return searchOne(sqlid, null);
    }

    @Override
    public <T> T searchOne(String sqlid, Object paramObj) {
        List<T> list = searchList(sqlid, paramObj);
        if (list == null || list.size() == 0) {
            return null;
        } else if (list.size() == 1) {
            return list.get(0);
        } else {
            //找得了多条记录，说明存在问题，需要给与使用者反馈（异常）
            throw new ResultCountException("it was expected to be null or one , but it was " + list.size());
        }
    }


    @Override
    public int modify(String sqlid, Object paramObj) {
        SqlHandler handler = configuration.getHandler(sqlid);
        String oldSql = handler.getOldSql();
        if ("update".equalsIgnoreCase(oldSql.trim().substring(0, 6))) {
            return jdbcExecutor.executeUpdate(sqlid, false, paramObj);
        }
        throw new SQLFormatException("not a update statement : " + oldSql);
    }

    @Override
    public int modify(String sqlid) {
        return modify(sqlid, null);
    }


    @Override
    public int remove(String sqlid) {
        return remove(sqlid, null);
    }

    @Override
    public int remove(String sqlid, Object paramObj) {
        SqlHandler handler = configuration.getHandler(sqlid);
        String oldSql = handler.getOldSql();
        if ("delete".equalsIgnoreCase(oldSql.trim().substring(0, 6))) {
            return jdbcExecutor.executeUpdate(sqlid, false, paramObj);
        }
        throw new SQLFormatException("not a delete statement : " + oldSql);
    }


    //----------------dao代理相关的api 可以直接将上面的api都实现--------------------
    @Override
    public <T> T getDaoProxy(Class<T> interfaceType) {
        // 获取当前session
        final SqlSession currentSession = this;
        //动态代理需要3个参数  ClassLoader ， interfaces , invocationHandler
        //动态代理执行时，会根据指定的接口产生代理类  （这个类以前没有，新产生的）
        //  新产生的类需要加载到jvm中才能使用， 利用类加载ClassLoader来加载。
        //      jdk提供了3个   bootstrap（jre） ， ext（ext.jar) , app（自定义）
        //类可以实现多个接口，所以需要提供接口数组（至少要有一个）
        //invocationhandler用来实现动态代理的调用功能。
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{interfaceType},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 获取当前SqlSession
                        SqlSession session = currentSession;
                        // 获取sqlId 类目+方法名
                        String sqlid = interfaceType.getName() + "." + method.getName();
                        SqlHandler handler = configuration.getHandler(sqlid);
                        // 获取sql语句
                        String sqlOption = handler.getOldSql().substring(0, 6);

                        Object paramObj = null;
                        // 设置sql参数
                        if (args != null && args.length > 0) {
                            // 有参数
                            paramObj = args[0];
                        }
                        Object value = null;
                        // 实现增删改查
                        if ("insert".equalsIgnoreCase(sqlOption)) {
                            value = session.save(sqlid, paramObj);
                        } else if ("update".equalsIgnoreCase(sqlOption)) {
                            value = session.modify(sqlid, paramObj);
                        } else if ("delete".equalsIgnoreCase(sqlOption)) {
                            value = session.remove(sqlid, paramObj);
                        } else if ("select".equalsIgnoreCase(sqlOption)) {
                            // 查询需要特别处理
                            // 获取目标方法的返回值
                            Class<?> returnType = method.getReturnType();
                            if (List.class.isAssignableFrom(returnType)) {
                                value = session.searchList(sqlid, paramObj);
                            } else {
                                value = session.searchList(sqlid, paramObj);
                            }
                        }
                        return value;
                    }
                });
    }
}




